package com.ruoyi.resume.service.impl;

import com.fasterxml.jackson.databind.ObjectMapper;
import com.ruoyi.resume.domain.model.*;
import com.ruoyi.resume.domain.model.resumeProfile.*;
import com.ruoyi.resume.domain.response.SplitResumeResultResp;
import com.ruoyi.resume.enums.SplitResumePrompt;
import com.ruoyi.resume.mapper.ResumeMapper;
import com.ruoyi.resume.mapper.ResumeRawDataMapper;
import com.ruoyi.resume.service.AsyncAnalysisResumeService;
import com.ruoyi.resume.service.FirstLabelingService;
import com.ruoyi.resume.service.ResumeService;
import com.ruoyi.resume.service.UploadResumeService;
import com.ruoyi.resume.utils.deepseek.DeepSeekV3Client;
import com.ruoyi.resume.utils.zhipu.Glm;
import com.ruoyi.resume.utils.ResumeParserUtil;
import com.ruoyi.resume.utils.ResumeProcessingUtils;
import com.ruoyi.resume.utils.minIO.MinIOUtil;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.transaction.support.TransactionSynchronizationAdapter;
import org.springframework.transaction.support.TransactionSynchronizationManager;
import org.springframework.web.multipart.MultipartFile;

import java.io.IOException;
import java.util.ArrayList;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import java.util.stream.Collectors;

@Service
public class UploadResumeServiceImpl implements UploadResumeService {

    private static final Logger logger = LoggerFactory.getLogger(UploadResumeServiceImpl.class);

    @Autowired
    private ThreadPoolExecutor executor;

    @Autowired
    private ResumeMapper resumeMapper;

    @Autowired
    private AsyncAnalysisResumeService asyncAnalysisResumeService;

    @Autowired
    private ObjectMapper objectMapper;

    @Autowired
    private ResumeRawDataMapper resumeRawDataMapper;

    @Autowired
    private MinIOUtil minIOUtil;

    @Autowired
    private FirstLabelingService firstLabelingService;

    @Autowired
    private ResumeService resumeService;

    @Override
    public SplitResumeResultResp processResume(MultipartFile file) {
        try {
            logger.info("开始处理简历...");
            String fileType = ResumeProcessingUtils.detectFileType(file);
            String extractedText = ResumeProcessingUtils.extractText(file, fileType);
            String resumeParts = splitResume(extractedText);
            Map<String, String> sectionMap = ResumeParserUtil.splitIntoSections(resumeParts);

            logger.info("开始保存简历到数据库中");
            Integer resumeId = saveResumeToDB(file, sectionMap);
            logger.info("开始异步程序——使用AI分析简历内容");
            asyncAnalysisResumeService.analysisUseAi(resumeParts, resumeId);

            // 保存PDF到云服务器中
            minIOUtil.uploadFileViaStream(file, "resume", resumeId + "/" + resumeId + ".pdf");
            logger.info("成功保存PDF以及文字版本简历到数据库，简历ID：" + resumeId);

            // 使用线程池并发处理各个部分
            CountDownLatch latch = new CountDownLatch(6);
            CompletableFuture<BasicInfo> basicInfoFuture = CompletableFuture.supplyAsync(() -> {
                BasicInfo basicInfo = splitBasicInfo(sectionMap.getOrDefault("基本信息", ""));
                latch.countDown();
                return basicInfo;
            }, executor);

            CompletableFuture<List<EducationBackground>> educationFuture = CompletableFuture.supplyAsync(() -> {
                List<EducationBackground> educationBackgrounds = splitEducationBackground(sectionMap.getOrDefault("教育经历", ""));
                latch.countDown();
                return educationBackgrounds;
            }, executor);

            CompletableFuture<List<WorkExperience>> workFuture = CompletableFuture.supplyAsync(() -> {
                List<WorkExperience> workExperiences = splitWorkExperience(sectionMap.getOrDefault("工作经历", ""));
                latch.countDown();
                return workExperiences;
            }, executor);

            CompletableFuture<List<InternshipExperience>> internshipFuture = CompletableFuture.supplyAsync(() -> {
                List<InternshipExperience> internshipExperiences = splitInternshipExperience(sectionMap.getOrDefault("实习经历", ""));
                latch.countDown();
                return internshipExperiences;
            }, executor);

            CompletableFuture<List<ProjectExperience>> projectFuture = CompletableFuture.supplyAsync(() -> {
                List<ProjectExperience> projectExperiences = splitProjectExperience(sectionMap.getOrDefault("项目经历", ""));
                latch.countDown();
                return projectExperiences;
            }, executor);

            CompletableFuture<List<RelatedSkill>> skillsFuture = CompletableFuture.supplyAsync(() -> {
                List<RelatedSkill> relatedSkills = splitRelatedSkills(sectionMap.getOrDefault("相关技能", ""));
                latch.countDown();
                return relatedSkills;
            }, executor);

            latch.await();
            BasicInfo basicInfo = basicInfoFuture.get();
            basicInfo.setId(resumeId);
            List<EducationBackground> educationBackgrounds = educationFuture.get();
            List<WorkExperience> workExperiences = workFuture.get();
            List<InternshipExperience> internshipExperiences = internshipFuture.get();
            List<ProjectExperience> projectExperiences = projectFuture.get();
            List<RelatedSkill> relatedSkills = skillsFuture.get();

            String skills = relatedSkills.stream()
                    .map(skill -> skill.getSkill() + ": " + skill.getSkillDescription())
                    .collect(Collectors.joining("\n"));
            basicInfo.setSkills(skills);

            // 创建并设置 SplitResumeResult
            SplitResumeResult splitResumeResult = new SplitResumeResult();
            splitResumeResult.setBasicInfo(basicInfo);
            splitResumeResult.setEducationBackgrounds(educationBackgrounds);
            splitResumeResult.setWorkExperiences(workExperiences);
            splitResumeResult.setInternshipExperiences(internshipExperiences);
            splitResumeResult.setProjectExperiences(projectExperiences);
            splitResumeResult.setRelatedSkills(relatedSkills);

            // 检查是否有重复简历
            Integer duplicateResumeId = checkDuplicateResume(basicInfo);

            // 构建返回结果
            SplitResumeResultResp resp = new SplitResumeResultResp();
            resp.setIsDuplicateResume(duplicateResumeId != null);
            resp.setSplitResumeResult(splitResumeResult);
            resp.setOldSplitResumeResult(duplicateResumeId != null ? resumeService.getResumeContentById(duplicateResumeId) : null);

            return resp;

        } catch (Exception e) {
            logger.error("处理简历时发生错误：{}", e.getMessage(), e);
            throw new RuntimeException("处理简历失败", e);
        }
    }



    public String splitResume(String extractedText) {
        logger.info("开始规则匹配进行简历分块...");

        // 定义需要识别的分类
        Pattern sectionPattern = Pattern.compile("^(教育经历|实习经历|工作经历|项目经历|相关技能|兴趣爱好|证书|语言能力)\\s*[:：]?\\s*$");

        // 处理文本
        String[] lines = extractedText.split("\n");
        Map<String, StringBuilder> sectionMap = new LinkedHashMap<>();
        String currentSection = "基本信息";  // 预设初始部分
        sectionMap.put(currentSection, new StringBuilder());

        // 遍历文本行
        for (String line : lines) {
            line = line.trim();
            if (line.isEmpty()) continue;

            // 识别新的分类标题
            Matcher matcher = sectionPattern.matcher(line);
            if (matcher.find()) {
                currentSection = matcher.group(1);
                sectionMap.putIfAbsent(currentSection, new StringBuilder());
            } else {
                // 追加内容
                sectionMap.get(currentSection).append(line).append("\n");
            }
        }

        // 规则匹配质量检测
        if (!isRuleBasedSplitReliable(sectionMap)) {
            logger.warn("规则匹配分块效果较差，回退到GLM模型进行简历分块...");
            return splitResumeWithGLM(extractedText);
        }

        // 构造最终格式化的输出
        StringBuilder result = new StringBuilder();
        for (Map.Entry<String, StringBuilder> entry : sectionMap.entrySet()) {
            String sectionTitle = entry.getKey();
            String sectionContent = entry.getValue().toString().trim();
            result.append(sectionTitle).append("：\n");
            result.append(sectionContent.isEmpty() ? "无" : sectionContent).append("\n\n");
        }

        logger.info("规则匹配分块完成");
        return result.toString().trim();
    }



    private boolean isRuleBasedSplitReliable(Map<String, StringBuilder> sectionMap) {
        int detectedSections = sectionMap.size();

        // 关键部分：基本信息、教育经历
        boolean hasBasicInfo = sectionMap.containsKey("基本信息") && sectionMap.get("基本信息").length() > 10;
        boolean hasEducation = sectionMap.containsKey("教育经历") && sectionMap.get("教育经历").length() > 10;

        // 如果分块数目少于 3，或者缺少基本信息/教育经历，认为规则匹配不可靠
        return detectedSections >= 3 && hasBasicInfo && hasEducation;
    }



    // 调用 GLM 模型进行整体简历分块
    public String splitResumeWithGLM(String extractedText) {
        logger.info("开始调用GLM模型进行简历分块...");
        String prompt = SplitResumePrompt.SPLIT_RESUME_WITH_GLM.getPrompt(extractedText);
        String result = Glm.chatGLM4(prompt);
        logger.info("成功调用GLM模型进行简历分块");
        return result;
    }

    private BasicInfo splitBasicInfo(String basicInfoText) {
        if (basicInfoText == null || basicInfoText.trim().isEmpty()) {
            return new BasicInfo();
        }
        String prompt = SplitResumePrompt.SPLIT_BASIC_INFO.getPrompt(basicInfoText);
        String modelOutput = Glm.chatGLM4(prompt);
        return ResumeParserUtil.parseBasicInfo(modelOutput);
    }

    private List<EducationBackground> splitEducationBackground(String educationBackgroundText) {
        if (educationBackgroundText == null || educationBackgroundText.trim().isEmpty()) {
            return new ArrayList<>();
        }
        String prompt = SplitResumePrompt.SPLIT_EDUCATION_BACKGROUND.getPrompt(educationBackgroundText);
        String modelOutput = Glm.chatGLM4(prompt);
        return ResumeParserUtil.parseEducationBackground(modelOutput);
    }

    private List<WorkExperience> splitWorkExperience(String workExperienceText) {
        if (workExperienceText == null || workExperienceText.trim().isEmpty()) {
            return new ArrayList<>();
        }
        String prompt = SplitResumePrompt.SPLIT_WORK_EXPERIENCE.getPrompt(workExperienceText);
        String modelOutput = Glm.chatGLM4(prompt);
        return ResumeParserUtil.parseWorkExperience(modelOutput);
    }

    private List<InternshipExperience> splitInternshipExperience(String internshipExperienceText) {
        if (internshipExperienceText == null || internshipExperienceText.trim().isEmpty()) {
            return new ArrayList<>();
        }
        String prompt = SplitResumePrompt.SPLIT_INTERNSHIP_EXPERIENCE.getPrompt(internshipExperienceText);
        String modelOutput = Glm.chatGLM4(prompt);
        return ResumeParserUtil.parseInternshipExperience(modelOutput);
    }

    private List<ProjectExperience> splitProjectExperience(String projectExperienceText) {
        if (projectExperienceText == null || projectExperienceText.trim().isEmpty()) {
            return new ArrayList<>();
        }
        String prompt = SplitResumePrompt.SPLIT_PROJECT_EXPERIENCE.getPrompt(projectExperienceText);
        String modelOutput = Glm.chatGLM4(prompt);
        return ResumeParserUtil.parseProjectExperience(modelOutput);
    }

    private List<RelatedSkill> splitRelatedSkills(String relatedSkillsText) {
        if (relatedSkillsText == null || relatedSkillsText.trim().isEmpty()) {
            return new ArrayList<>();
        }
        String prompt = SplitResumePrompt.SPLIT_PROFESSIONAL_SKILLS.getPrompt(relatedSkillsText);
        String modelOutput = Glm.chatGLM4(prompt);
        return ResumeParserUtil.parseRelatedSkills(modelOutput);
    }

    private Integer saveResumeToDB(MultipartFile file, Map<String, String> sectionMap) throws IOException {
        // 1. 获取文件类型
        String fileType = file.getContentType();

        // 2. 将文件转换为二进制数据
        byte[] fileData = file.getBytes();

        // 3. 将 Map 转换为 JSON 字符串
        String resumeData = objectMapper.writeValueAsString(sectionMap);

        // 4. 创建 ResumeRawData 实体对象
        ResumeRawData resumeRawData = new ResumeRawData();
        resumeRawData.setPdfData(fileData);
        resumeRawData.setResumeData(resumeData);
        resumeRawData.setFileType(fileType);

        resumeRawDataMapper.saveResume(resumeRawData);

        // 5. 获取主键 ID
        Integer generatedId = resumeRawData.getId();

        // 6. 记录日志
        logger.info("成功保存原始简历到数据库中，文件类型：{}，生成的主键 ID：{}", fileType, generatedId);

        // 7. 返回主键 ID
        return generatedId;
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public SplitResumeResult saveStructuredResume(SplitResumeResult resumeData) {
        resumeMapper.insertResume(resumeData.getBasicInfo());
        Integer resumeId = Math.toIntExact(resumeData.getBasicInfo().getId());
        resumeMapper.insertEducations(resumeId, resumeData.getEducationBackgrounds());
        resumeMapper.insertWorks(resumeId, resumeData.getWorkExperiences());
        resumeMapper.insertInternships(resumeId, resumeData.getInternshipExperiences());
        resumeMapper.insertProjects(resumeId, resumeData.getProjectExperiences());

        TransactionSynchronizationManager.registerSynchronization(new TransactionSynchronizationAdapter() {
            @Override
            public void afterCommit() {
                try {
                    List<Map<String, Integer>> response = firstLabelingService.firstLabeling(resumeData);
                    logger.info("打标签返回结果：" + response);
                } catch (Exception e) {
                    e.printStackTrace();
                }
            }
        });
        return resumeData;
    }


    public Integer checkDuplicateResume(BasicInfo basicInfo) {
        List<Integer> duplicateIds = resumeMapper.selectDuplicateResumeIds(basicInfo.getName(),
                basicInfo.getPhone(),
                basicInfo.getEmail());
        // 如果存在重复记录，返回最大的 ID；否则返回 null
        return duplicateIds.isEmpty() ? null : duplicateIds.get(0);
    }
}
